package wiiusej.hardwareControllers;

import java.io.IOException;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;

import wiiusej.Wiimote;
import wiiusej.apptest.WiimoteEventListener;
import wiiusej.apptest.generalTasks.EnableMotionTask;

public class PermanentRumbleController extends PermanentController{

	
	private static final String fileURL = "src/test/java/wiiusej/resources/logs/tasks/RumbleController";
	
	private BlockingQueue<Integer> fromEnergyAndRealQ = new LinkedBlockingQueue<Integer>();
	private int lastFreqLevel = 1250;
		
	//commands
	private static final int TURN_OFF = 0;
	private static final int TURN_ON = 1;
	
	//state variable
	private AtomicBoolean activated = new AtomicBoolean();
	
	//Concurrent Main Class
	protected static ExecutorService exe;
	
	Future actuator;
	
	//Logger
	Logger fileLogger2;
	
	public PermanentRumbleController(Wiimote wiimote, BlockingQueue<Integer> toRumbleQ, ExecutorService exe) {
		
		super(wiimote);
		this.exe = exe;
		
		activated.set(false);
		
		this.fromEnergyAndRealQ = toRumbleQ;
		
		PropertyConfigurator.configure("log4j.properties");
		this.fileLogger2 = Logger.getLogger("rumbleController");
		
		fileLogger2.info("INIT RUMBLE CONTROLLER - Activated: " + activated);
		
		/*
		PatternLayout pattern = 
			new PatternLayout("%r %-5p %c %x - %m%n");		
		
		try {			
			//Creating a file for every connected wiimote
			FileAppender fileappend=new FileAppender(pattern,fileURL + wiimote.getId() + ".log");
			fileLogger.addAppender(fileappend);
		} catch (IOException e) {
			e.printStackTrace();
		}*/		
		
	}

	@Override
	public void run() {
		
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		while(true){
			
			Integer command;
			
			//wait until get a new command from RealTime or Energy
			try {
				fileLogger2.info("@@@ Listening Queue");
				command = fromEnergyAndRealQ.take();					
				fileLogger2.info("@@@ Received at: " + tsFormat.format(System.currentTimeMillis()));
				
				if(command == 1){			
					vibrationAction();
				}
				
				//receiveCommand(command);
				//uploadFreq(command);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
			
			
			
			
//			if(activated.get()){
//				
//				fileLogger2.info("@@@ Activating Rumble - State: " + activated + " at: " + 
//						tsFormat.format(System.currentTimeMillis()));
//				
//				myWiimote.activateRumble();
//				
//				try {
//					//we could receive and update
//					Thread.sleep(50);
//					//command = fromEnergyAndRealQ.poll(75, TimeUnit.MILLISECONDS);
//					//CheckCommand(command);
//					
//								
//					
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				
//				
//				myWiimote.deactivateRumble();
//				activated.set(false);
//				fileLogger2.info("@@@ Deactivating Rumble - State: " + activated + " at: " + 
//						tsFormat.format(System.currentTimeMillis()));
//				
//				if(!fromEnergyAndRealQ.isEmpty()){
//					
//					fileLogger2.info("@@@ Clearing Queue - Elements Removed: " + 
//							fromEnergyAndRealQ.size() + " Elements: " + fromEnergyAndRealQ);
//					fromEnergyAndRealQ.clear();						
//				}
//				
//				fileLogger2.info("");
//			}
//			
//			try {
//				Thread.sleep(100);
//				
//				if(!fromEnergyAndRealQ.isEmpty()){
//					
//					fileLogger2.info("@@@ Clearing Queue - Elements Removed: " + 
//							fromEnergyAndRealQ.size() + " Elements: " + fromEnergyAndRealQ);
//					fromEnergyAndRealQ.clear();						
//				}
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
				/*
				try {
					
					long elapsed = 0;
					long wait = lastFreqLevel;
					
					Timestamp t1 = new Timestamp(System.currentTimeMillis());
					fileLogger2.info("@@@ Get Time t1: " + t1);
					
					
					do{						
						
						fileLogger2.info("****************************************");
						fileLogger2.info("@@@ INIT RUMBLE BUCLE ");
						fileLogger2.info("");
						fileLogger2.info("@@@ Wait: " + wait); 
						
						command = fromEnergyAndRealQ.poll(wait, TimeUnit.MILLISECONDS);
						Timestamp t2 = new Timestamp(System.currentTimeMillis());
						
						if(command != null){
							fileLogger2.info("@@@ Command: " + command);							
						}else{
							fileLogger2.info("@@@ No Command Received");
						}
						
						fileLogger2.info("@@@ Get Time t2: " + t2);
						
						
						if(command != null){
							
							receiveCommand(command);
							
							fileLogger2.info("@@@ Activated: " + activated);
							
							if(!activated.get()){
								fileLogger2.info("");
								break;
							}
							
							if(activated.get() && uploadFreq(command)){
								elapsed = wait;
								fileLogger2.info("@@@ Uploading Frequency");
								break;
							}
							
							if(activated.get() && !uploadFreq(command)){
								elapsed = t2.getTime() - t1.getTime();
								wait = wait - elapsed;
								t1.setTime(System.currentTimeMillis());
								
								fileLogger2.info("@@@ Elapsed: " + elapsed + 
										" Wait: " + wait + " new t1: " + t1);
							}
							
							
							
						}else{
							//timeout exceed
							elapsed = wait;
						}	
						
						fileLogger2.info("");
						
					}while(activated.get() && elapsed < wait);
					
					//we could receive and update
					//command = fromEnergyAndRealQ.poll(lastFreqLevel, TimeUnit.MILLISECONDS);				
					
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}*/
			//}
			
			
			
			
		}
		
		/*
		while(true){			
			
			Integer command = fromEnergyQ.poll();
			
			
			if(command != null){
				
				receiveCommand(command);				
			
				int state = processCommand(command);
				
				switch(state){
				
					//command that cancels the rumble - the motion is not correct
					case 0:
						try {
							actuator.cancel(true);		
							//actuator.get();
							
						//} catch (InterruptedException e) {
							//fileLogger2.info("Rumble CANCELED by new COMMAND");
						//} catch (ExecutionException e) {
							//e.printStackTrace();
						} catch(CancellationException e){
							fileLogger2.info("Rumble CANCELED by new COMMAND");
							//fileLogger.info("Actuator State: " + actuator.isCancelled());
						}
						
						
						
						fileLogger2.info("");
						break;
				

					//change of frequency makes change the rumble performance
					case 1: 
						fileLogger2.info("Rumble CANCELED by new FREQUENCY");
						
						
						try {
							actuator.cancel(true);
							//actuator.get();
						//} catch (InterruptedException e) {
							//fileLogger2.info("Rumble CANCELED by new FREQUENCY");
						//} catch (ExecutionException e) {
							//e.printStackTrace();
						}catch(CancellationException e){
							fileLogger2.info("Rumble CANCELED by new FREQUENCY");
						}
						fileLogger2.info("");
						
						makeVibration();
						
						break;
						
					case 2:						
						fileLogger2.info("");
						//do nothing
						break;
						
					case 3:
						fileLogger2.info("");
						
						makeVibration();
												
						break;
					
					
						
				}
				
				
			
			}
				
			
			
			

			
			
			
			
			
		
			
		}*/
		
		
		
	}
	
	private void vibrationAction(){
		//SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		
		long elapsed;
		
		myWiimote.activateRumble();
		Timestamp t1 = new Timestamp(System.currentTimeMillis());
		
		try {
			//we could receive and update
			Thread.sleep(10);
			//command = fromEnergyAndRealQ.poll(75, TimeUnit.MILLISECONDS);
			//CheckCommand(command);						
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		myWiimote.deactivateRumble();
		Timestamp t2 = new Timestamp(System.currentTimeMillis());
		
		elapsed = t2.getTime() - t1.getTime();		
		fileLogger2.info("@@@ Elapsed Vibrating Time: " + elapsed);
		
		
//		try {
//			Thread.sleep(10);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		
//		myWiimote.activateRumble();
//		
//		try {
//			//we could receive and update
//			Thread.sleep(75);
//			//command = fromEnergyAndRealQ.poll(75, TimeUnit.MILLISECONDS);
//			//CheckCommand(command);						
//			
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}			
//		
//		myWiimote.deactivateRumble();
//		
//		myWiimote.activateRumble();
//		
//		try {
//			//we could receive and update
//			Thread.sleep(75);
//			//command = fromEnergyAndRealQ.poll(75, TimeUnit.MILLISECONDS);
//			//CheckCommand(command);						
//			
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}			
//		myWiimote.deactivateRumble();
	}
	
	private void CheckCommand(Integer command){
		
		if(command != null){
			receiveCommand(command);
			uploadFreq(command);			
		}
		
	}
	
	private int processCommand(Integer command){
		
		fileLogger2.info("Activated: " + activated);
		
		//command that cancels the rumble - the motion is not correct
		if(!activated.get() && actuator!=null){
			fileLogger2.info("STATE: 0");
			return 0;
		}
		
		//change of frequency makes change the rumble performance
		if(activated.get() && uploadFreq(command) && actuator!=null){
			fileLogger2.info("STATE: 1");
			return 1;
		}
		
		if(!activated.get()){
			fileLogger2.info("STATE: 2");
			return 2;
		}
		
		
		fileLogger2.info("STATE: 3");
		// continue rumbling
		return 3;
		
		
	}
	private void makeVibration() {		
		/*
		fileLogger2.info("*** VIBRATING ****");		
		actuator = 
    		exe.submit(new RumblerAtomicActuator(myWiimote, lastFreqLevel));		
		fileLogger2.info("");*/
	}
	
	
	private boolean uploadFreq(Integer freq){

		if(freq != 0 && freq != 1){		
			
			if(lastFreqLevel != freq){
				fileLogger2.info("New Freq Value: " + freq);				
				lastFreqLevel = freq;
				return true;
			}
						
		}
				
		fileLogger2.info("");
		return false;
			
		
	}
	
	private void receiveCommand(Integer command){
		
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		
		fileLogger2.info("@@@ Received command: " + command);
		
		switch(command){
		
			case TURN_OFF:
				if(activated.get()){
					//myWiimote.deactivateRumble();
					fileLogger2.info("@@@ deactivating rumble at: " + tsFormat.format(System.currentTimeMillis())
							+ " Current Freq: " + lastFreqLevel);
					activated.set(false);					
				}else{
					fileLogger2.info("@@@ Still Deactivated");
				}
					
				
				break;
				
			case TURN_ON:
				
				if(!activated.get()){
					//myWiimote.activateRumble();
					fileLogger2.info("@@@ activating rumble at: " + tsFormat.format(System.currentTimeMillis()) + 
							"Current Freq: " + lastFreqLevel);
					activated.set(true);					
				}else{
					fileLogger2.info("@@@ Still Activated");
				}
				
											
				break;
		
		}
	}

}
